////////////////////////////////////////////////////////////////////////////////
//
//  The MIT License
//
//  Copyright (c) 2008 - 2009. Dimarik
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////

package ru.goodcore.logging.targets {

import ru.goodcore.errors.GenericError;
import ru.goodcore.errors.GenericErrorCode;
import ru.goodcore.logging.ILogger;
import ru.goodcore.logging.LogEvent;
import ru.goodcore.logging.LogEventLevel;
import ru.goodcore.logging.formatters.ITargetMessageFormatter;
import ru.goodcore.logging.formatters.LineFormatter;


/**
 *  This class provides the basic functionality required by the logging framework
 *  for a target implementation.
 *  It handles the validation of filter expressions and provides a default level
 *  property.
 *  No implementation of the <code>logEvent()</code> method is provided.
 *
 *
 * @author				Dimarik
 * @version				1.0
 * @playerversion		9
 * @langversion			3.0
 */

public class AbstractLoggingTarget implements ILoggingTarget {

	include "../../../../includes/core/Version.as";

	//-------------------------------------------------------------------------
	//
	//  Constructor
	//
	//-------------------------------------------------------------------------

	/**
	 *  Constructor.
	 */
	public function AbstractLoggingTarget() {
		super();
	}

	//-------------------------------------------------------------------------
	//
	//  Variables
	//
	//-------------------------------------------------------------------------

	/**
	 *  @private
	 *  Count of the number of loggers this target is listening to. When this
	 *  value is zero changes to the filters property shouldn't do anything
	 */
	private var _loggerCount:	uint = 0;


	//-------------------------------------------------------------------------
	//
	//  Properties
	//
	//-------------------------------------------------------------------------

	//---------------------------------
	//  filters
	//---------------------------------

	/**
	 *  @private
	 *  Storage for the filters property.
	 */
	private var _filters:Array = [ "*" ];

	[Inspectable(category="General", arrayType="String")]

	/**
	 *  In addition to the <code>level</code> setting, filters are used to
	 *  provide a psuedo-hierarchical mapping for processing only those events
	 *  for a given category.
	 *  <p>
	 *  Each logger belongs to a category.
	 *  By convention these categories map to the fully-qualified class name in
	 *  which the logger is used.
	 *  For example, a logger that is logging messages for the
	 *  <code>mx.rpc.soap.WebService</code> class, uses
	 *  "mx.rpc.soap.WebService" as the parameter to the
	 *  <code>Log.getLogger()</code> method call.
	 *  When messages are sent under this category only those targets that have
	 *  a filter which matches that category receive notification of those
	 *  events.
	 *  Filter expressions can include a wildcard match, indicated with an
	 *  asterisk.
	 *  The wildcard must be the right-most character in the expression.
	 *  For example: rpc~~, mx.~~, or ~~.
	 *  If an invalid expression is specified, a <code>InvalidFilterError</code>
	 *  is thrown.
	 *  If <code>null</code> or [] is specified, the filters are set to the
	 *  default of ["~~"].
	 *  </p>
	 *  <p>For example:
	 *     <pre>
	 *           var traceLogger:ILoggingTarget = new TraceTarget();
	 *           traceLogger.filters = ["mx.rpc.~~", "mx.messaging.~~"];
	 *           Log.addTarget(traceLogger);
	 *     </pre>
	 *  </p>
	 */
	public function get contentFilters():Array {
		return this._filters;
	}

	/**
	 *  @private
	 *  This method will make sure that all of the filter expressions specified
	 *  are valid, and will throw <code>InvalidFilterError</code> if any are not.
	 */
	public function set contentFilters(value:Array):void {
		if (value && value.length > 0)
		{
			// a valid filter value will be fully qualified or have a wildcard
			// in it.  the wild card can only be located at the end of the
			// expression.  valid examples  xx*, xx.*,  *
			var filter:		String;
			var index:		int;
			var message:	String;
	/*
			for (var i:uint = 0; i<value.length; i++)
			{
				filter = value[i];
				  // check for invalid characters
				if (Log.hasIllegalCharacters(filter))
				{
					throw new Error('logging charsInvalid');
				}

				index = filter.indexOf("*");
				if ((index >= 0) && (index != (filter.length -1)))
				{
					throw new Error('logging charPlacement');
				}
			} // for
	*/
		}
		else
		{
			// if null was specified then default to all
			value = ["*"];
		}

	/*
		if (_loggerCount > 0)
		{
			Log.removeTarget(this);
			_filters = value;
			Log.addTarget(this);
		}
		else
		{
			_filters = value;
		}
	*/
	}

	//---------------------------------
	//  level
	//---------------------------------

	/**
	 *  @private
	 *  Storage for the level property.
	 */
	private var _level:		int = LogEventLevel.ALL;

	/**
	 *  Provides access to the level this target is currently set at.
	 *  Value values are:
	 *    <ul>
	 *      <li><code>LogEventLevel.FATAL (int.MAX_VALUE)</code> designates events 
	 *      that are very harmful and will eventually lead to application failure</li>
	 *
	 *      <li><code>LogEventLevel.ERROR (4)</code> designates error events that might
	 *      still allow the application to continue running.</li>
	 *
	 *      <li><code>LogEventLevel.WARN (3)</code> designates events that could be
	 *      harmful to the application operation</li>
	 *
	 *      <li><code>LogEventLevel.INFO (2)</code> designates informational messages
	 *      that highlight the progress of the application at
	 *      coarse-grained level.</li>
	 *
	 *      <li><code>LogEventLevel.DEBUG (1)</code> designates informational
	 *      level messages that are fine grained and most helpful when
	 *      debugging an application.</li>
	 *
	 *      <li><code>LogEventLevel.ALL (0)</code> intended to force a target to
	 *      process all messages.</li>
	 *    </ul>
	 */
	public function get level():int {
		return this._level;
	}

	/**
	 *  @private
	 */
	public function set level(value:int):void {
	/*
		// A change of level may impact the target level for Log.
		Log.removeTarget(this);
		_level = value;
		Log.addTarget(this);
	*/       
	}

	//---------------------------------
	//  formatter
	//---------------------------------

	[InstanceType("ru.goodcore.logging.formatters.LineFormatter")]
	/**
	 * @private
	 */
	protected var _formatter:	ITargetMessageFormatter;

	[Inspectable(category="General", defaultValue="LineFormatter")]
	/**
	 * Default value is LineFormatter
	 * @private
	 */
	public function get formatter():ITargetMessageFormatter {
		if ( !this._formatter ) {
			this._formatter = new LineFormatter();
		}
		return this._formatter;
	}

	public function set formatter(value:ITargetMessageFormatter):void {
		// do nothing
	}

	//-------------------------------------------------------------------------
	//
	//  Methods
	//
	//-------------------------------------------------------------------------

	/**
	 *  Sets up this target with the specified logger.
	 *  This allows this target to receive log events from the specified logger.
	 *
	 *  @param logger The ILogger that this target should listen to.
	 */
	public function addLogger(logger:ILogger):void {
		if (logger) {
			this._loggerCount++;
			logger.addEventListener(LogEvent.LOG, this.handler_log);
		}
	}

	/**
	 *  Stops this target from receiving events from the specified logger.
	 *
	 *  @param logger The ILogger that this target should ignore.
	 */
	public function removeLogger(logger:ILogger):void {
		if (logger) {
			this._loggerCount--;
			logger.removeEventListener(LogEvent.LOG, this.handler_log);
		}
	}

	/**
	 *  This method handles a <code>LogEvent</code> from an associated logger.
	 *  A target uses this method to translate the event into the appropriate
	 *  format for transmission, storage, or display.
	 *  This method will be called only if the event's level is in range of the
	 *  target's level.
	 *
	 *  <p><b><i>NOTE: Descendants must override this method to make it useful.</i></b></p>
	 *  
	 *  @param event An event from an associated logger.
	 */
	public function logEvent(event:LogEvent):void
	{
		throw new GenericError(GenericErrorCode.METHOD_MUST_BE_OVERRIDDEN);
	}

	//-------------------------------------------------------------------------
	//
	//  Event handlers
	//
	//-------------------------------------------------------------------------

	/**
	 *  @private
	 *  This method will call the <code>logEvent</code> method if the level of the
	 *  event is appropriate for the current level.
	 */
	private function handler_log(event:LogEvent):void {
		if (event.level >= this.level)
			this.logEvent(event);
	}

}
}
